package org.ak47.spring.boot.grpc.autoconfigure;


import com.google.common.collect.Lists;
import io.grpc.ClientInterceptor;
import io.grpc.ClientInterceptors;
import io.grpc.ManagedChannel;
import io.grpc.netty.NettyChannelBuilder;
import lombok.extern.slf4j.Slf4j;
import org.ak47.spring.boot.grpc.GrpcChannelProperties;
import org.ak47.spring.boot.grpc.GrpcChannelsProperties;

import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * <p> 默认grpc通道工厂实现 </p>
 *
 * @author hexiaohong
 */
@Slf4j
public class DefaultGrpcChannelFactory implements GrpcChannelFactory
{

    private final GrpcChannelsProperties properties;

    private final GlobalClientInterceptorRegistry globalClientInterceptorRegistry;

    private final Map<String, ManagedChannel> grpcClientChannels = new HashMap<>();

    public DefaultGrpcChannelFactory(GrpcChannelsProperties properties,
                                     GlobalClientInterceptorRegistry globalClientInterceptorRegistry)
    {
        this.properties = properties;
        this.globalClientInterceptorRegistry = globalClientInterceptorRegistry;
    }

    @Override
    public ManagedChannel createChannel(String name)
    {
        return this.createChannel(name, null);
    }

    @Override
    public ManagedChannel createChannel(String name, List<ClientInterceptor> interceptors)
    {
        GrpcChannelProperties channelProperties = properties.getChannel(name);

        ManagedChannel existChannel = grpcClientChannels.get(name);
        if (channelProperties.isSingleton() && existChannel != null)
        {
            return existChannel;
        }

        NettyChannelBuilder builder = NettyChannelBuilder.forAddress(
            channelProperties.getServerHost(), channelProperties.getServerPort()).keepAliveTime(
                channelProperties.getKeepAliveDelay(), TimeUnit.SECONDS).keepAliveTimeout(
                    channelProperties.getKeepAliveTimeout(), TimeUnit.SECONDS);
        if (channelProperties.isPlaintext())
        {
            builder.usePlaintext();
        }

        log.info("Build grpc client channel: {}", channelProperties);
        ManagedChannel channel = builder.build();
        if (channel == null)
        {
            throw new IllegalArgumentException("use parameters build channel failure");
        }
        if (!channel.isTerminated() && !channel.isShutdown())
        {
            log.info("connect to server[{}], host={}, port={}", name,
                channelProperties.getServerHost(), channelProperties.getServerPort());
        }

        List<ClientInterceptor> globalInterceptorList = globalClientInterceptorRegistry.getClientInterceptors();
        Set<ClientInterceptor> interceptorSet = new HashSet<>();
        if (globalInterceptorList != null && !globalInterceptorList.isEmpty())
        {
            interceptorSet.addAll(globalInterceptorList);
        }
        if (interceptors != null && !interceptors.isEmpty())
        {
            interceptorSet.addAll(interceptors);
        }
        ManagedChannel intercept = (ManagedChannel)ClientInterceptors.intercept(channel,
            Lists.newArrayList(interceptorSet));
        grpcClientChannels.put(name, intercept);
        return intercept;
    }
}
